package main;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ExecutorService;

import quadtree.AbstractNode.Cell;
import quadtree.AbstractNodeElement;
import quadtree.DoublePoint;
import quadtree.NewQuadTreeElement;
import quadtree.NewQuadTreeNode;
import quadtree.point.PointNode;

public class GameTree implements Commons{
	
	public DrawTree drawTreeA = null;
	public DrawTree drawTreeB = null;
	public DumbTree dumbTree = null;
	public SpriteTree spritesTree = null;
	
	/* These ArrayList containers are used to swap sprites between the SpriteTree 
	* and the DumbTree in the event that a Sprite changes from being collidable
	* to not collidable
	*/
	public ArrayList<Sprite> spriteTreeToDumbTree;
	public ArrayList<Sprite> dumbTreeToSpriteTree;
	
	ExecutorService execSvc;	
	boolean debug;
	
	public GameTree(){
		this.debug = false;
		drawTreeA = new DrawTree();
		drawTreeB = new DrawTree();
		dumbTree = new DumbTree();
		dumbTree.setDrawTreeA(drawTreeA);
		dumbTree.setDrawTreeB(drawTreeB);
		spriteTreeToDumbTree = new ArrayList<Sprite>();
		dumbTreeToSpriteTree = new ArrayList<Sprite>();
		spritesTree = new SpriteTree();
		spritesTree.setDrawTreeA(drawTreeA);
		spritesTree.setDrawTreeB(drawTreeB);
		spritesTree.setSpriteTreeToDumbTree(spriteTreeToDumbTree);
		
	}
	
	public void refreshDrawTree(boolean cycleA){
		if(cycleA){
			this.drawTreeA.createDrawTree();
		}
		else{
			this.drawTreeB.createDrawTree();
		}
	}
	
	public void setDebug(boolean d){
		this.debug = d;
	}
	
	public boolean isDrawTreeEmpty(boolean cycleA){
		if(cycleA){
			return drawTreeA.isEmpty();
		}
		else{
			return drawTreeB.isEmpty();
		}
	}
	
	public boolean isDumbTreeEmpty(){
		if(dumbTree == null){
			return true;
		}
		return false;		
	}
	
	public boolean isSpritesTreeEmpty(){
		if(spritesTree == null){
			return true;
		}
		return false;		
	}
	
	
	public void debugDraw(Graphics2D g, ImageObserver image_observer){
		if(!isSpritesTreeEmpty()){
			NewQuadTreeNode<Sprite> rootNode = spritesTree.getRootNode();
			debugDrawTree(rootNode, g, image_observer);			
		}
	}
	
	public void drawEveryThing(Graphics2D g, ImageObserver image_observer, boolean cycleA){
		if(debug){
			//debugDraw(g, image_observer);
		}
		if(cycleA){
			if(!isDrawTreeEmpty(cycleA)){
				this.drawTreeB.draw(g, image_observer);	
			}
		}
		else{
			if(!isDrawTreeEmpty(!cycleA)){
				this.drawTreeA.draw(g, image_observer);	
			}
		}
			
	}
	
	public void insertSprite(DoublePoint p, Sprite s){
		if(s.isCollidable()){
			spritesTree.insert(p,s);			
		}
		else{			
			dumbTree.insert(s);
		}		
	}
	
	public void thinkEverything(boolean a, long time){
		refreshDrawTree(a);
		boolean cycleA = a;
		boolean cycleB = !a;
		
		dumbTreeToSpriteTree = dumbTree.think(a, time);
		if(!isSpritesTreeEmpty()){
			NewQuadTreeNode<Sprite> rootNode = spritesTree.getRootNode();
			spritesTree.thinkTree(rootNode, cycleA, cycleB, time);			
		}
		
		Iterator<Sprite> i = dumbTreeToSpriteTree.iterator();
		while(i.hasNext()){
			Sprite s = i.next();
			spritesTree.insert(new DoublePoint(s.getX(), s.getY()),s); 
		}
		Iterator<Sprite> i2 = spriteTreeToDumbTree.iterator();
		while(i.hasNext()){
			Sprite s = i2.next();
			dumbTree.insert(s); 
		}
		dumbTreeToSpriteTree.clear();
		spriteTreeToDumbTree.clear();	
	}
	
	
	
	@SuppressWarnings("unchecked")
	public void debugDrawTreeNode(NewQuadTreeNode node, Graphics2D g, ImageObserver image_observer) {
		try{
			Vector<NewQuadTreeElement<Sprite>> elements = (Vector<NewQuadTreeElement<Sprite>>) node
			.getElements();
			for (NewQuadTreeElement<Sprite> element : elements) {
				element.getElement().setG(g);
				element.getElement().setImgObserver(image_observer);
				//execSvc.execute(element.getElement());
				element.getElement().draw();

			}
			if(debug){
				//Draws the quadtree for a visual reference
				g.setColor(Color.RED);
				g.drawLine((int)node.getStartCoordinates().x, (int)node.getStartCoordinates().y, 
						(int)node.getStartCoordinates().x + (int)node.getBounds().width,
						(int)node.getStartCoordinates().y);
				g.drawLine((int)node.getStartCoordinates().x, (int)node.getStartCoordinates().y, 
						(int)node.getStartCoordinates().x,
						(int)node.getStartCoordinates().y + (int)node.getBounds().height);
				g.drawLine((int)node.getStartCoordinates().x + (int)node.getBounds().width, (int)node.getStartCoordinates().y, 
						(int)node.getStartCoordinates().x + (int)node.getBounds().width,
						(int)node.getStartCoordinates().y + (int)node.getBounds().height);
				g.drawLine((int)node.getStartCoordinates().x, (int)node.getStartCoordinates().y + (int)node.getBounds().height, 
						(int)node.getStartCoordinates().x + (int)node.getBounds().width,
						(int)node.getStartCoordinates().y + (int)node.getBounds().height);
				g.setColor(Color.white);
				Font small = new Font("Helvetica", Font.BOLD, 10);
				g.setFont(small);
				g.drawString("Size " + (int)node.getElements().size(), (int)node.getStartCoordinates().x + 10, (int)node.getStartCoordinates().y + 10);
				//profiles.printToScreen(g, 0, 0);
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}


	protected void debugDrawTree(NewQuadTreeNode<Sprite> node, Graphics2D g, ImageObserver image_observer) {

		debugDrawTree(node.TopLeft, g, image_observer);
		debugDrawTree(node.TopRight, g, image_observer);
		debugDrawTree(node.BottomLeft, g, image_observer);
		debugDrawTree(node.BottomRight, g, image_observer);

		debugDrawTreeNode(node, g,image_observer);
	}

}
